package com.thh.zookeeperlock.zk;

import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.exception.ZkNodeExistsException;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/*
 * @Author 徐柯老师
 * @TEL/微信 15074816437
 * @学习网站 http://www.itbooking.net
 * @Description zookeeper分布式锁
 * @Date
 * @Param
 * @return
 **/
public class ZKDistributeLock implements Lock {

    // znode节点
    private String lockPath;
    //zkclient客户端
    private ZkClient zkClient;


    // 初始化客户端和目录
    public ZKDistributeLock(String lockPath){
        if(lockPath==null || lockPath.trim().equals("")){
            throw  new IllegalArgumentException("参数lockpath不能为空......");
        }
        this.lockPath = lockPath;
        zkClient = new ZkClient("localhost:2181");
        zkClient.setZkSerializer(new MyZkSerializer());
    }


    @Override
    public void lock() {
        if(!tryLock()){
            //如果已经获取到锁了，那么久挂起.等待
            waitForLock();//这里一定要找个可以阻塞线程的机制，那么久是：CountDownLatch或者CyclicBarrier
            //再次尝试获取lock
            lock();
        }
    }

    // 等待挂起锁，找个能阻塞线程执行的。
    private void waitForLock(){
        // 定义线程阻塞
        CountDownLatch countDownLatch = new CountDownLatch(1);

        IZkDataListener listener = new IZkDataListener() {
            @Override
            public void handleDataChange(String dataPath, Object data) throws Exception {
            }
            @Override
            public void handleDataDeleted(String dataPath) throws Exception {
                //如果节点被删除，立马释放。什么叫节点被删除呢？就是去调用unlock的时候执行 `zkClient.delete(lockPath);` 的时候回触发
                //说明释放锁了，那么放行让别的线程去执行获取锁操作。
                System.out.println("********************收到节点删除***************************");
                countDownLatch.countDown();
            }
        };

        // 监听节点变化
        zkClient.subscribeDataChanges(lockPath,listener);

        // 否则阻塞自己
        if(this.zkClient.exists(lockPath)){
            try{
                //被挂起
                countDownLatch.await();
            }catch(Exception ex){
               ex.printStackTrace();
            }
        }
        // 取消注册
        zkClient.unsubscribeDataChanges(lockPath,listener);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {//不会阻塞
        try{
            //创建临时节点，如果节点已经存在就会出现异常ZkNoNodeException，利用这个zk的这个特性
            //刚好满足排他性。
            zkClient.createEphemeral(lockPath);
        }catch(ZkNodeExistsException ex){
            return false;
        }

        return true;
    }


    @Override
    public void unlock() {
        zkClient.delete(lockPath);
    }



    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}
